import 'dart:collection';

import 'package:simulator/typedef/notifier.dart';

enum RunState {
  debug,
  inmigrate,
  internalError,
  ioError,
  paused,
  postmigrate,
  prelaunch,
  finishMigrate,
  restoreVm,
  running,
  saveVm,
  shutdown,
  suspended,
  watchdog,
  guestPanicked,
  colo,
  max
}

final class RunStateTransition {
  final RunState from;
  final RunState to;
  const RunStateTransition(this.from, this.to);

  static const List<RunStateTransition> runstateTransitionsDef = [
    RunStateTransition(RunState.prelaunch, RunState.inmigrate),
    RunStateTransition(RunState.prelaunch, RunState.suspended),
    RunStateTransition(RunState.debug, RunState.running),
    RunStateTransition(RunState.debug, RunState.finishMigrate),
    RunStateTransition(RunState.debug, RunState.prelaunch),
    RunStateTransition(RunState.inmigrate, RunState.internalError),
    RunStateTransition(RunState.inmigrate, RunState.ioError),
    RunStateTransition(RunState.inmigrate, RunState.paused),
    RunStateTransition(RunState.inmigrate, RunState.running),
    RunStateTransition(RunState.inmigrate, RunState.shutdown),
    RunStateTransition(RunState.inmigrate, RunState.suspended),
    RunStateTransition(RunState.inmigrate, RunState.watchdog),
    RunStateTransition(RunState.inmigrate, RunState.guestPanicked),
    RunStateTransition(RunState.inmigrate, RunState.finishMigrate),
    RunStateTransition(RunState.inmigrate, RunState.prelaunch),
    RunStateTransition(RunState.inmigrate, RunState.postmigrate),
    RunStateTransition(RunState.inmigrate, RunState.colo),
    RunStateTransition(RunState.internalError, RunState.paused),
    RunStateTransition(RunState.internalError, RunState.finishMigrate),
    RunStateTransition(RunState.internalError, RunState.prelaunch),
    RunStateTransition(RunState.paused, RunState.running),
    RunStateTransition(RunState.paused, RunState.finishMigrate),
    RunStateTransition(RunState.paused, RunState.postmigrate),
    RunStateTransition(RunState.paused, RunState.prelaunch),
    RunStateTransition(RunState.paused, RunState.colo),
    RunStateTransition(RunState.paused, RunState.suspended),
    RunStateTransition(RunState.prelaunch, RunState.running),
    RunStateTransition(RunState.prelaunch, RunState.finishMigrate),
    RunStateTransition(RunState.prelaunch, RunState.inmigrate),
    RunStateTransition(RunState.finishMigrate, RunState.running),
    RunStateTransition(RunState.finishMigrate, RunState.paused),
    RunStateTransition(RunState.finishMigrate, RunState.postmigrate),
    RunStateTransition(RunState.finishMigrate, RunState.prelaunch),
    RunStateTransition(RunState.finishMigrate, RunState.colo),
    RunStateTransition(RunState.finishMigrate, RunState.internalError),
    RunStateTransition(RunState.finishMigrate, RunState.ioError),
    RunStateTransition(RunState.finishMigrate, RunState.shutdown),
    RunStateTransition(RunState.finishMigrate, RunState.suspended),
    RunStateTransition(RunState.finishMigrate, RunState.watchdog),
    RunStateTransition(RunState.finishMigrate, RunState.guestPanicked),
    RunStateTransition(RunState.restoreVm, RunState.running),
    RunStateTransition(RunState.restoreVm, RunState.prelaunch),
    RunStateTransition(RunState.restoreVm, RunState.suspended),
    RunStateTransition(RunState.colo, RunState.running),
    RunStateTransition(RunState.colo, RunState.prelaunch),
    RunStateTransition(RunState.colo, RunState.shutdown),
    RunStateTransition(RunState.running, RunState.debug),
    RunStateTransition(RunState.running, RunState.internalError),
    RunStateTransition(RunState.running, RunState.ioError),
    RunStateTransition(RunState.running, RunState.paused),
    RunStateTransition(RunState.running, RunState.finishMigrate),
    RunStateTransition(RunState.running, RunState.restoreVm),
    RunStateTransition(RunState.running, RunState.saveVm),
    RunStateTransition(RunState.running, RunState.shutdown),
    RunStateTransition(RunState.running, RunState.watchdog),
    RunStateTransition(RunState.running, RunState.guestPanicked),
    RunStateTransition(RunState.running, RunState.colo),
    RunStateTransition(RunState.saveVm, RunState.running),
    RunStateTransition(RunState.saveVm, RunState.suspended),
    RunStateTransition(RunState.shutdown, RunState.paused),
    RunStateTransition(RunState.shutdown, RunState.finishMigrate),
    RunStateTransition(RunState.shutdown, RunState.prelaunch),
    RunStateTransition(RunState.shutdown, RunState.colo),
    RunStateTransition(RunState.debug, RunState.suspended),
    RunStateTransition(RunState.running, RunState.suspended),
    RunStateTransition(RunState.suspended, RunState.running),
    RunStateTransition(RunState.suspended, RunState.finishMigrate),
    RunStateTransition(RunState.suspended, RunState.prelaunch),
    RunStateTransition(RunState.suspended, RunState.colo),
    RunStateTransition(RunState.suspended, RunState.paused),
    RunStateTransition(RunState.suspended, RunState.saveVm),
    RunStateTransition(RunState.suspended, RunState.restoreVm),
    RunStateTransition(RunState.suspended, RunState.shutdown),
    RunStateTransition(RunState.watchdog, RunState.running),
    RunStateTransition(RunState.watchdog, RunState.finishMigrate),
    RunStateTransition(RunState.watchdog, RunState.prelaunch),
    RunStateTransition(RunState.watchdog, RunState.colo),
    RunStateTransition(RunState.guestPanicked, RunState.running),
    RunStateTransition(RunState.guestPanicked, RunState.finishMigrate),
    RunStateTransition(RunState.guestPanicked, RunState.prelaunch),
  ];
}

class SimuRunState {
  static final SimuRunState _instance = SimuRunState._internal();
  SimuRunState._internal();

  static SimuRunState getInstance() => _instance;

  static RunState currentRunState = RunState.prelaunch;

  List<List<bool>> runstateValidTransitions = List.generate(RunState.max.index,
      (index) => List.generate(RunState.max.index, (index) => false));

  static late LinkedList<Notifier> exitNotifiers;

  static exitNotifiersHook() {
    for (var entry in exitNotifiers) {
      entry.notify(notifier: entry);
    }
  }

  void runstateInit() {
    exitNotifiers = LinkedList();
    for (var element in RunStateTransition.runstateTransitionsDef) {
      runstateValidTransitions[element.from.index][element.to.index] = true;
    }
  }
}
